#!/usr/local/bin/python
# Rob Dennis - Lurchington Q. Delacroix
# Keasby - TV renamer
# Project Homepage: http://code.google.com/p/keasby/

#--- Python Imports
import unittest
import logging
import os
import zipfile
import collections
from os.path import join as pjoin
from contextlib import closing

#--- Keasby Imports
from keasby_base import KeasbyError
from keasby_options import kopt

log = logging.getLogger("keasby_logger")


class KeasbyCacheError(KeasbyError):
    """
    Generic KeasbyCacheError
    """

class KeasbyCache(collections.Container):
    """
    Abstract Base class for for interaction with stored resources
    """
    path = property(lambda self: self.__path)

    def __init__(self, path=None):

        if path == None:
            self.__path = kopt.cl_opts.cache_dir
        else:
            self.__path = kopt.cl_opts.cache_dir
            self.__path = self._cast_path(path)

        if not os.path.exists(self.path):
            os.makedirs(self.path)

        assert os.path.exists(self.path)
        log.debug("cache directory = %s", self.path)

    def open(self, path, mode="rb"):
        """
        return an open file descriptor for the given relative path
        """

        return open(self._cast_path(path), mode)

    def read_zip(self, zipped_file, zipped_path=None):
        """
        Return a file-object for the zipfile with specified relative path\
        OR open file object. zipped_path shall be specified to give a path \
        to a specific file within the zipfile. If zipped_path is None\
        and there's only 1 zipped file, return a file-object for that.
        """
        #"%s.xml"%language is the important file for tvdb

        if isinstance(zipped_file, file):
            _zip = zipfile.ZipFile(zipped_file)

        else:
            _zip = zipfile.ZipFile(self.open(self._cast_path(zipped_file),
                                                mode="rb"))

        if zipped_path is None:
            zipped_files = _zip.infolist()
            if len(zipped_files) == 1:
                zipped_path = zipped_files[0].filename
                log.debug("no path specified for zipped file, assuming: %s",
                            zipped_path)
            else:
                raise KeasbyCacheError("unable to assume zipped file path"+
                                        "choose one of:"+
                                        "\n%s"%"\n".join((tmp.filename
                                                      for tmp in zipped_files)))

        return _zip.open(zipped_path)


    def read(self, path):
        """
        calls open(path) with mode of "rb"
        """
        if not os.path.exists(path):
            raise KeasbyCacheError("specified path %s does not exist"%path)
        return self.open(path, "rb")

    def write(self, path, mode="wb"):
        """
        calls open(path) with given mode (append/write). If directories\
        in path do not exist, make them
        """

        if "r" in mode:
            raise KeasbyCacheError("invalid write mode string \"%s\"", mode)

        path = self._cast_path(path)

        #make sure the parent directory exists
        pdir, _ = os.path.split(path)

        if not os.path.exists(pdir):
            log.debug("creating parent directory: %s", pdir)
            os.makedirs(pdir)

        return self.open(path, mode)

    def dump(self, path):
        """
        returns the contents of path using the read()
        """

        with self.read(self._cast_path(path)) as dumpobj:
            return dumpobj.read()

    def cache(self, path, data, mode="wb"):
        """
        Cache the contents of data to the specified path. If data has a read()
        method, write that to the path, otherwise assume a string and write it\
        to the path.
        """

        with self.write(path, mode) as cacheobj:
            if isinstance(data, basestring):
                cacheobj.write(data)
            elif hasattr(data, "read"):
                log.debug("assuming cache data (%s) is file-like", type(data))
                cacheobj.write(data.read())
            elif hasattr(data, "next"):
                log.debug("assuming cache data (%s) is an iterable", type(data))
                cacheobj.writelines(data)

#---Utility

    def _cast_path(self, path):
        """
        determine if the given path is absolute or relative and return an\
        absolute path
        """

        if self.path in path:
            log.debug("assuming %s is an absolute path", path)
            pass
        else:
            log.debug("assuming %s is a relative path", path)
            path = pjoin(self.path, path)
            log.debug("using: %s", path)

        return path

    def abspath(self, path):
        """
        return the absolute path from given relative path, with the cache\
        path as the start
        """

        return pjoin(self.path, path)


    def __contains__(self, path):

        return os.path.exists(self._cast_path(path))







